package hibatis.support.parse;

import hibatis.annotation.ColumnValue;
import hibatis.annotation.ValueParam;
import hibatis.ext.AdditionalColumnValue;
import hibatis.ext.InterceptorContext;
import hibatis.support.Reflection;
import hibatis.support.StringUtils;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationAttributes;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by huangdachao on 2018/6/29 14:57.
 * 更新时的数据表列与参数值对应关系
 */
public class ColumnValuePair {
    private String column;
    private String placeholder;
    private Set<Table> referTables; // 解析placeholder时，引用的其它表

    public ColumnValuePair(String column, String placeholder) {
        this.column = column;
        this.placeholder = placeholder;
        this.referTables = new HashSet<>();
    }

    public ColumnValuePair(String column, String placeholder, Set<Table> referTables) {
        this.column = column;
        this.placeholder = placeholder;
        this.referTables = referTables;
    }

    public ColumnValuePair(ColumnValue cv, EntityMeta em, String propertyName, String paramName) {
        if (!cv.column().isEmpty()) {
            this.column = cv.column();
        } else {
            String field = propertyName;
            if (!cv.field().isEmpty()) {
                field = cv.field();
            }

            TableColumn tc = em.getFieldsMap().get(field);
            if (tc == null) {
                throw new RuntimeException("无法解析的字段：" + em.getEntityClass().getName() + "." + field);
            }
            this.column = tc.column;
        }

        this.placeholder = cv.placeholder();
        if (!this.placeholder.isEmpty()) {
            this.placeholder = this.placeholder.replaceAll("\\$@", paramName);
            this.referTables = Table.parse(this.placeholder, em);
        } else {
            this.placeholder = paramName;
        }
    }

    public static void mergeFromColumnValues(ColumnValue[] array, EntityMeta em, List<ColumnValuePair> pairs) {
        Arrays.stream(array).forEach(cv -> {
            String column;
            if (!cv.column().isEmpty()) {
                column = cv.column();
            } else if (!cv.field().isEmpty()) {
                TableColumn tc = em.getFieldsMap().get(cv.field());
                if (tc == null) {
                    throw new RuntimeException("无法解析的字段：" + em.getEntityClass().getName() + "." + cv.field());
                }
                column = tc.column;
            } else {
                throw new RuntimeException("无效的@ColumnValue：" + cv + "。实体类：" + em.getEntityClass());
            }

            if (cv.placeholder().isEmpty()) {
                throw new RuntimeException("请指定@ColumnValue的placeholder属性：" + cv);
            }
            String placeholder = cv.placeholder();
            ColumnValuePair cvp = new ColumnValuePair(column, placeholder, Table.parse(placeholder, em));
            merge(pairs, cvp, cv.override());
        });
    }

    public static void mergeFromInterceptorContext(InterceptorContext context, Map<String, Object> additionalParams, List<ColumnValuePair> exists) {
        for (int i = 0; i < context.getAdditionalColumnValues().size(); i++) {
            AdditionalColumnValue acv = context.getAdditionalColumnValues().get(i);
            String paramName = "__v" + i;
            additionalParams.put(paramName, acv.getVal());

            String column;
            EntityMeta em = EntityMeta.get(context.getEntityClass());
            if (!StringUtils.isEmpty(acv.getColumn())) {
                column = acv.getColumn();
            } else if (StringUtils.isEmpty(acv.getEntityField())) {
                TableColumn tc = em.getFieldsMap().get(acv.getEntityField());
                if (tc != null) {
                    column = tc.column;
                } else {
                    throw new RuntimeException("没有找到映射字段：" + acv);
                }
            } else {
                throw new RuntimeException("无效的AdditionalColumnValue：" + acv);
            }

            ColumnValuePair cvp;
            if (!StringUtils.isEmpty(acv.getPlaceholder())) {
                Set<Table> tables = Table.parse(acv.getPlaceholder(), em);
                cvp = new ColumnValuePair(column, acv.getPlaceholder().replaceAll("\\$@", paramName), tables);
            } else {
                cvp = new ColumnValuePair(column, paramName);
            }
            merge(exists, cvp, acv.isOverride());
        }
    }

    @SuppressWarnings("unchecked")
    public static List<ColumnValuePair> parseArgs(EntityMeta em, Method m, Object[] args, String[] argNames) {
        List<ColumnValuePair> list = new ArrayList<>();
        Set<TableColumn> updateColumns = em.getUpdateColumns();
        if (args.length == 1 && em.getEntityClass().isAssignableFrom(args[0].getClass())) {
            return updateColumns.stream().map(tc -> new ColumnValuePair(tc.column, em.getFieldMeta().get(tc).getField().getName()))
                .collect(Collectors.toList());
        }

        Annotation[][] annotations = m.getParameterAnnotations();
        for (int i = 0; i < args.length; i++) {
            ValueParam vp = Reflection.findAnnotation(annotations[i], ValueParam.class);
            if (vp != null) {
                if (vp.ignoreOnNull() && args[i] == null) {
                    continue;
                }
                if (vp.ignoreOnZero() && Reflection.isNumberZero(args[i])) {
                    continue;
                }

                AnnotationAttributes attrs = AnnotatedElementUtils.getMergedAnnotationAttributes(
                    AnnotatedElementUtils.forAnnotations(vp), ValueParam.class);
                String field = attrs.getString("field");
                String placeholder = vp.placeholder().isEmpty() ? argNames[i] : vp.placeholder().replace("$@", argNames[i]);
                Set<Table> referTables = Table.parse(placeholder, em);
                if (!vp.column().isEmpty()) {
                    list.add(new ColumnValuePair(vp.column(), placeholder, referTables));
                } else if (!StringUtils.isEmpty(field)) {
                    TableColumn tc = em.getFieldsMap().get(field);
                    if (tc == null) {
                        throw new RuntimeException("没找到可更新字段：" + em.getTable() + "." + field);
                    }
                    list.add(new ColumnValuePair(tc.column, placeholder, referTables));
                } else if (args[i] instanceof Map) { // process Map
                    Map<String, Object> map = (Map<String, Object>) args[i];
                    for (String key : map.keySet()) {
                        TableColumn tc = em.getFieldsMap().get(key);
                        if (tc != null) {
                            list.add(new ColumnValuePair(tc.column, argNames[i] + "." + key));
                        }
                    }
                } else { // process as bean
                    Object arg = args[i];
                    String argName = argNames[i];
                    Reflection.resolveProperties(args[i].getClass()).forEach(member -> {
                        ColumnValue cv = Reflection.getPropertyAnnotation(member, ColumnValue.class);
                        Object val = Reflection.getPropertyValue(arg, member);
                        String paramName = argName + "." + member.getName();
                        if (cv != null) {
                            if (cv.ignore()) {
                                return;
                            } else if (cv.ignoreOnNull() && val == null) {
                                return;
                            } else if (cv.ignoreOnZero() && Reflection.isNumberZero(val)) {
                                return;
                            }

                            list.add(new ColumnValuePair(cv, em, member.getName(), paramName));
                        } else {
                            TableColumn tc = em.getFieldsMap().get(member.getName());
                            if (tc != null) {
                                list.add(new ColumnValuePair(tc.column, paramName));
                            }
                        }
                    });
                }
            }
        }
        return list;
    }

    private static void merge(List<ColumnValuePair> list, ColumnValuePair pair, boolean override) {
        Iterator<ColumnValuePair> iterator = list.iterator();
        while (iterator.hasNext()) {
            ColumnValuePair cvp = iterator.next();
            if (cvp.column.equals(pair.column)) {
                if (override) {
                    iterator.remove();
                    break;
                } else {
                    return;
                }
            }
        }
        list.add(pair);
    }

    public String getColumn() {
        return column;
    }

    public void setColumn(String column) {
        this.column = column;
    }

    public String getPlaceholder() {
        return placeholder;
    }

    public void setPlaceholder(String placeholder) {
        this.placeholder = placeholder;
    }

    public Set<Table> getReferTables() {
        return referTables;
    }

    public void setReferTables(Set<Table> referTables) {
        this.referTables = referTables;
    }
}
